Skip to content

[2.x] Introduce configurable merge strategy for shared props #744

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: 2.x
Choose a base branch
from

Conversation

jimbrouwer
Copy link

@jimbrouwer jimbrouwer commented Jun 7, 2025

This PR introduces deep merging functionality for shared Inertia props to address a key limitation in the current server-side adapter behavior.

The proposed change makes prop handling in Laravel Inertia apps more flexible and reliable, aligning actual behavior with Inertia's official documentation.


🚨 Problem

The official documentation states:

"Inertia's server-side adapters all provide a method of making shared data available for every request. This is typically done outside of your controllers. Shared data will be automatically merged with the page props provided in your controller."

While this implies that shared and controller-level props are seamlessly merged, the actual behavior is a shallow merge, causing keys with similar names to be overwritten instead combined.


✅ Solution

This PR introduces a configurable merge strategy for managing shared Inertia props, providing flexibility and control over how props are combined.

  • Maintains existing behavior by default with the ShallowMergeStrategy
  • Allows enabling a DeepMergeStrategy either globally or on a per-call basis
  • Designed to be extensible, enabling easy customization through custom merge strategies

This allows middleware and controllers to safely contribute to the same shared props without data loss or key collisions.

Inertia::setSharedPropMerger(new DeepMergeStrategy);

Inertia::share(['can' => ['view']]);

Inertia::render('Index', ['can' => ['edit']]);

// Results before:
['can' => ['edit']]

// Results after:
['can' => ['view', 'edit']]

Changes

  • Added a MergeStrategy interface to define merging behavior for shared props
  • Implemented ShallowMergeStrategy as the default merge strategy to preserve existing behavior
  • Implemented DeepMergeStrategy as an optional merge strategy for recursive merging
  • Introduced setSharedPropMerger() method to allow global configuration on runtime of the merge strategy
  • Updated ResponseFactory to support configurable merge strategies when sharing props and rendering components

Why this should not be a breaking change

  • Default merge behavior remains unchanged with ShallowMergeStrategy
  • New merge strategies are opt-in and require explicit configuration
  • Existing code has minimal changes, ensuring backward compatibility

@jimbrouwer jimbrouwer force-pushed the deep-merges-shared-props branch 3 times, most recently from e948b6f to f95595c Compare June 8, 2025 00:07
- Add DeepMergesSharedProps trait to flatten and deep merge overlapping shared props within a single Inertia response
- Update ResponseFactory to apply deep merging when rendering components and sharing props
@jimbrouwer jimbrouwer force-pushed the deep-merges-shared-props branch from f95595c to e511ff2 Compare June 8, 2025 00:25
@pascalbaljet
Copy link
Contributor

This is a significant change in how props are handled, and I would not expect this behavior when using shared props. Merging is also handled in the frontend. I understand the idea of merging data from the controller into shared props, but I believe it should be more explicit if it should even be a feature of this package.

@pascalbaljet
Copy link
Contributor

One example that might break apps is when you share data for a select dropdown:

Inertia::share('someOptions', Model::query()->someGeneralScope()->get());

But then, in another controller or middleware (e.g., for an admin section), you would overwrite it:

Inertia::render('Dashboard', [
    'someOptions' => Model::query()->moreSpecificScope()->get(),
]);

@jimbrouwer
Copy link
Author

This is a significant change in how props are handled, and I would not expect this behavior when using shared props. Merging is also handled in the frontend. I understand the idea of merging data from the controller into shared props, but I believe it should be more explicit if it should even be a feature of this package.

Thanks for the feedback!

Personally, I find the example use-case a bit uncommon. It suggests intentionally sharing global props, then overriding or limiting them in controllers. In my experience, the opposite is more typical: global props provide consistent data (like base permissions, such as access to certain modules within the application), and controllers extend them contextually - for example by adding page-specific permissions.

However, I fully agree to your concern. The proposed behavior could be unexpected or unwanted in some scenarios. Making it explicit would definitely help avoid confusion.

If you'd like to I’d be happy to revise the PR. What do you think about the following solutions?


Option 1: mergeShare / deepMergeShare method

This method would handle the deep merging behavior and return the full shared prop array.

Inertia::mergeShare($key, $value);

Inertia::render('Index', Inertia::mergeShare([]));

Option 2: Merge strategy pattern

This introduces an extensible solution. By default the ShallowMergeStrategy could be used to preserve the original behavior, which can be overwritten globally or per call.

It also means the DeepMergeStrategy could be left out of the package, if that’s preferred. Since teams could implement their own strategy as needed.

Inertia::share([], new DeepMergeStrategy);

Inertia::withMergeStrategy(new DeepMergeStrategy)->render('Index', []);

Let me know what you think! I'll be happy to adjust the approach to better fit the direction of the package.

- Adds support for merge strategy pattern when sharing props
- Default behavior remains unchanged via `ShallowMergeStrategy`
- Allows consumers to override globally or per call with a custom strategy
@jimbrouwer jimbrouwer changed the title [2.x] Deep merge shared props in ResponseFactory [2.x] Introduce configurable merge strategy for shared props Jun 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants